package com.sen.uaa.config;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.sen.common.common.exception.BusinessException;
import com.sen.common.common.inter.RespCodeEnum;
import com.sen.common.common.response.MessageResult;
import com.sen.common.common.response.ResultMsgEnum;
import com.sen.common.common.util.JacksonUtils;
import com.sen.uaa.exception.ValidateCodeException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;
import java.security.Principal;
import java.util.Map;

/**
 * oauth-token拦截器
 * 2. 统一返回token格式
 * @author esjiang
 */
@Slf4j
@Component
@Aspect
public class OauthTokenAspect implements InitializingBean {

    private String[] keywords = {"password","keyNum"};

    private static final String USER_NAME = "username" ;
    private static final String TENANT_CODE = "tenantCode" ;

    private static final String ENTITY_TYPE = "entityType" ;

    private static final String SYSTEM_CODE = "systemCode" ;

    private void handleKeyword(Map<String, String> parameters){
        for (String keyword : keywords) {
            if (parameters.containsKey(keyword)){
                String value = parameters.get(keyword);
                String decryptValue = rsa.decryptStr(value, KeyType.PrivateKey, Charset.forName("utf-8"));
                parameters.put(keyword,decryptValue) ;
            }
        }
    }

    private RSA rsa ;

    @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))")
    public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        Principal principal = (Principal) args[0];
        if (!(principal instanceof Authentication)) {
            throw new InsufficientAuthenticationException(
                    "There is no client authentication. Try adding an appropriate authentication filter.");
        }
        Map<String, String> parameters = (Map<String, String>) args[1];
        try {
            handleKeyword(parameters) ;
            String grantType = parameters.get(OAuth2Utils.GRANT_TYPE);
            ParamsContextHolder.setParams(parameters);
            Object proceed = joinPoint.proceed();
            if ("authorization_code".equals(grantType)) {
                return proceed;
            } else {
                ResponseEntity<OAuth2AccessToken> responseEntity = (ResponseEntity<OAuth2AccessToken>) proceed;
                OAuth2AccessToken body = responseEntity.getBody();
                log.info("OauthTokenAspect result:{}", JacksonUtils.toJSONString(MessageResult.success(body)));
                ParamsContextHolder.clear();
                return ResponseEntity
                        .status(HttpStatus.OK)
                        .body(MessageResult.success(body));
            }
        } catch (Exception e) {
            log.error("授权错误", e);
            if (e instanceof InvalidGrantException || e instanceof UsernameNotFoundException){
                MessageResult result = MessageResult.reqFailed(ResultMsgEnum.bad_credentials);
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result) ;
            }
            if (e instanceof ValidateCodeException){
                MessageResult result = MessageResult.reqFailed(MessageResult.build(400, e.getMessage(), false));
                return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result);
            }
            if (e instanceof InternalAuthenticationServiceException){
                InternalAuthenticationServiceException exception = (InternalAuthenticationServiceException) e;
                Throwable cause = exception.getCause();
                if (cause instanceof BusinessException){
                    RespCodeEnum respCodeEnum = ((BusinessException) e.getCause()).getRespCodeEnum();
                    return ResponseEntity
                            .status(HttpStatus.BAD_REQUEST)
                            .body(respCodeEnum);
                }
            }
            return ResponseEntity
                    .status(HttpStatus.BAD_REQUEST)
                    .body(MessageResult.reqFailed(ResultMsgEnum.FAILED));
        }
    }

    @Value("${publicKey}")
    private String publicKey ;

    @Value("${privateKey}")
    private String privateKey ;

    @Override
    public void afterPropertiesSet() throws Exception {
        rsa = SecureUtil.rsa(privateKey,publicKey) ;
    }
}
